/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package org.sais.eswn.net;

import java.util.logging.Level;
import java.util.logging.Logger;
import org.sais.eswn.core.GameCore;
import org.sais.eswn.core.Player;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.util.Calendar;
import javax.swing.JOptionPane;

/**
 *
 * @author Romulus
 */
public class HostSock extends Sock {

    private ServerSocket mServer;
    private int mPort = -1;
    private boolean ready = false;
    private boolean isApplicant = false;
    private ISockCallback mParent;
    private DeliverWatchServer mDeliverWatchServer;
    private Socket mDeliverSocket;

    public HostSock(ISockCallback caller, int port, String playername) throws IOException {
        mCaller = caller;
        mDispatcher = new HostMessageDispatcher(this);
        this.mPort = port;
        this.mPlayerName = playername;
        mServer = new ServerSocket();
        mServer.setReuseAddress(true);
        mServer.bind(new InetSocketAddress(port));
        this.start();
    }

    @Override
    synchronized public void run() {
        try {
            // Only accept participants here!
            while (!isApplicant) {
                mSocket = mServer.accept();
                scan();
            }
            mServer.close();
            mCore = GameCore.startPlay(mSocket, this, true);
            mCore.setPlayerName(Player.ICH, mPlayerName);
            mCore.send("$PLAYERNAME:" + mPlayerName);
            mCore.setUITitle(GameCore.sDisplayTitle + " (HOST)");
            mCaller.setMessage("Stand by Ready.");
            mCaller.setVisible(false);
            send(mSocket, "$VERSION:" + GameCore.sVersion);
            mDeliverWatchServer = new DeliverWatchServer(mPort, mPort - 1);
            mDeliverSocket = new Socket();
            mDeliverSocket.connect(new InetSocketAddress("localhost", mPort - 1));
            new DeliverSock(mDeliverSocket).start();
            ready = true;
            scan();
        } catch (SocketException ex) {
            ex.printStackTrace();
        } catch (Exception ex) {
            ex.printStackTrace();
        }
    }

    @Override
    public void scan() {
        try {
            BufferedReader in = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "Unicode"));
            String instr;
            while ((instr = in.readLine()) != null) {
                instr = Fix(instr);
                if (debug) {
                    Calendar c = Calendar.getInstance();
                    System.out.println("HOSTSOC " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) + "/ " + instr);
                }
                if (instr.startsWith("$IWANTTOPLAY:")) {
                    send(mSocket, "$YOUAREAPPLICANT:");
                    isApplicant = true;
                    return;
                }
                if (instr.startsWith("$IWANTTOWATCH:")) {
                    send(mSocket, "$NOGAMEYET:");
                    isApplicant = false;
                    mSocket.close();
                    return;
                }
                //Messages dispatch
                if (ready) {
                    deliver(instr, false);
                    mDispatcher.dispatch(instr);
                }
            }
        } catch (SocketException ex) {
            if (ex.getMessage().equals("Connection reset")) {
                JOptionPane.showMessageDialog(null, ex.getMessage(), "Scan() in HostSock", JOptionPane.ERROR_MESSAGE);
            }
        } catch (IOException ex) {
            //JOptionPane.showMessageDialog(null, ex.getClass()+"\n"+ex.getMessage(), "Scan() in HostSock", JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
        } catch (NullPointerException ex) {
            //JOptionPane.showMessageDialog(null, ex.getClass()+"\n"+ex.getMessage(), "Scan() in HostSock", JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
        }
    }

    @Override
    public void closeSocket() {
        try {
            if (mSocket != null) {
                mSocket.shutdownOutput();
                mSocket.close();
                mSocket = null;
            }
            if (mServer != null) {
                mServer.close();
                mServer = null;
            }
            if (mDeliverSocket != null) {
                mDeliverSocket.close();
                mDeliverSocket = null;
            }
            if (mDeliverWatchServer != null) {
                mDeliverWatchServer.stopAccept();
                mDeliverWatchServer = null;
            }
        } catch (IOException ex) {
            //JOptionPane.showMessageDialog(null, ex.getClass()+"\n"+ex.getMessage(), "closeSocket() in HostSock", JOptionPane.ERROR_MESSAGE);
            ex.printStackTrace();
        }
    }

    @Override
    public void send(Socket soc, String message) {
        deliver(message, true);
        super.send(soc, message);
    }

    private void deliver(String message, boolean isHost) {
        if (mDeliverSocket == null) {
            return;
        }
        if (isHost) {
            super.send(mDeliverSocket, "$WATCHHOST: " + message);
        } else {
            super.send(mDeliverSocket, "$WATCHCLIENT: " + message);
        }
    }

    class DeliverSock extends Sock {

        public DeliverSock(Socket soc) {
            this.mSocket = soc;
            this.mDispatcher = new HostMessageDispatcher(HostSock.this);
        }

        @Override
        public void run() {
            scan();
        }

        @Override
        public void closeSocket() {
            try {
                mSocket.close();
            } catch (IOException ex) {
                Logger.getLogger(HostSock.class.getName()).log(Level.SEVERE, null, ex);
            }
        }

        @Override
        public void scan() {
            try {
                BufferedReader in = new BufferedReader(new InputStreamReader(mSocket.getInputStream(), "Unicode"));
                String instr;
                while ((instr = in.readLine()) != null) {
                    instr = Fix(instr);
                    if (debug) {
                        Calendar c = Calendar.getInstance();
                        System.out.println("DELIVERSOC " + c.get(Calendar.HOUR_OF_DAY) + ":" + c.get(Calendar.MINUTE) + ":" + c.get(Calendar.SECOND) + "/ " + instr);
                    }
                    this.mDispatcher.dispatch(instr);
                }
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
    }
}
